﻿using System;
using Gadgeteer.Interfaces;
using Microsoft.SPOT.IO;
using Microsoft.SPOT.Net.NetworkInformation;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using GTI = Gadgeteer.Interfaces;

namespace Gadgeteer.Modules.Sytech
{
    /// <summary>
    /// A EthernetSD Gadgeteer module
    /// </summary>
    public class EthernetSD : GTM.Module.NetworkModule, ISDCardModule
    {
        // This example implements  a driver in managed code for a simple Gadgeteer module.  The module uses a 
        // single GTI.InterruptInput to interact with a sensor that can be in either of two states: low or high.
        // The example code shows the recommended code pattern for exposing the property (IsHigh). 
        // The example also uses the recommended code pattern for exposing two events: EthernetSDHigh, EthernetSDLow. 
        // The triple-slash "///" comments shown will be used in the build process to create an XML file named
        // GTM.ManufacturerName.EthernetSD. This file will provide Intellisense and documention for the
        // interface and make it easier for developers to use the EthernetSD module.        

        #region SD Card Properties and Events

        /// <summary>
        /// Delegate for Card Insert/eject events
        /// see <see cref="MediaChangeHandler"/> event
        /// 
        /// </summary>
        /// <param name="sender">sender</param>
        /// <param name="sdCard">storage Card instance</param>
        /// <param name="cardInserted">true if card insert, false if eject</param>
        public delegate void MediaChangeHandler(object sender, StorageDevice sdCard, bool cardInserted);

        /// <summary>
        /// SD Card Media Changed event
        /// Fired when SD card inserted or ejected
        /// </summary>
        public event MediaChangeHandler OnMediaChanged;

        /// <summary>
        /// Property
        /// True if a SD card is inserted, false if not
        /// </summary>
        public bool CardInserted { private set; get; }

        /// <summary>
        /// Property
        /// Returns the current SD Card object
        /// Null if no card inserted
        /// </summary>
        public StorageDevice SDCard { private set; get; }

        #endregion


        // Note: A constructor summary is auto-generated by the doc builder.
        /// <summary></summary>
        /// <param name="socketNumber">The socket that this module is plugged in to.</param>
        public EthernetSD(int socketNumber)
        {
            // This finds the Socket instance from the user-specified socket number.  
            // This will generate user-friendly error messages if the socket is invalid.
            // If there is more than one socket on this module, then instead of "null" for the last parameter, 
            // put text that identifies the socket to the user (e.g. "S" if there is a socket type S)
            Socket socket = Socket.GetSocket(socketNumber, true, this, null);

            //debug
            DebugPrintEnabled = true;

            //Validate Socket choice
            socket.EnsureTypeIsSupported('Z',this);

            // Attach Meridian Native SPI use into Gadgeteer SPI mechanism, allowing sharing
            InitMeridianSPI(ref socket);

            InitEthernet();

            InitSDCard();

        }

       // private NetworkInterface NetworkSettings;
        private void InitEthernet()
        {
             NetworkInterface[] adaptors;
            adaptors = NetworkInterface.GetAllNetworkInterfaces();
            if (adaptors.Length > 0)
            {
                this.NetworkSettings = adaptors[0];
            }
        }

        private void InitMeridianSPI(ref Socket socket)
        {
            // need to reservere our 'manufacturer specific' pins
            socket.ReservePin(Socket.Pin.Three, this);  //sd cd
            socket.ReservePin(Socket.Pin.Four, this);   //sd card detect
            socket.ReservePin(Socket.Pin.Five, this);  // ethernet spi cs
            socket.ReservePin(Socket.Pin.Six, this);    // ethernet interupt

            // now we need to attach to the SPI mechanism, allowing the bus
            // to be shared - but not directly accessing it, this is done at a 
            // native level for the SPI Ethernet and SD card

           
            SPI MeridianSPI = new SPI(socket, null, SPI.Sharing.Shared, this);
        }


        #region SDCard

        private void InitSDCard()
        {
            // Attach the insert - eject handlers
            RemovableMedia.Eject += new EjectEventHandler(RemovableMedia_Eject);
            RemovableMedia.Insert += new InsertEventHandler(RemovableMedia_Insert);

            // Test if a card is inserted ( left in on power up )
            // as we would have missed the event - it occured before this module
            // was created
            VolumeInfo[] Volumes = VolumeInfo.GetVolumes();
            if (Volumes.Length > 0)            
            {
                // card inserted, fire event to initialise card object
                // will fire public event - but unlikely anything
                //anything has been able to attach to it yet, but we
                //need to update card insert status and object so we are ready
                // for a manual card get
                VolumeInfo test = Volumes[0];
                MediaEventArgs args = new MediaEventArgs(test, DateTime.Now);
                RemovableMedia_Insert(this, args);
            }

        }


        /// <summary>
        /// Card Inserted event generator.
        /// Change Card Inserted status to true and
        /// create a card object
        /// 
        /// Fire the OnMediaChanged Event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void RemovableMedia_Insert(object sender, MediaEventArgs e)
        {

            // media event args has the volume
   
            VolumeInfo test = e.Volume;
            if ( test != null)
            {
               // create the new card object
                SDCard = new StorageDevice(test);
                CardInserted = true;    // update the card insert status

                if (DebugPrintEnabled)
                {
                    DebugPrint("SD Card Inserted");
                    DebugPrint("Root Dir ; " + SDCard.RootDirectory);         
                }
                // fire the event if connected
                // this is not on the dispatcher thread
                if (OnMediaChanged != null)
                    {
                        OnMediaChanged(this, SDCard, true);
                    }
            }
        }

        /// <summary>
        /// Card removed event generator
        /// Update the Card Inserted status and clear the card object
        /// Fire the OnMediaChanged event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void RemovableMedia_Eject(object sender, MediaEventArgs e)
        {
            SDCard = null;
            CardInserted = false;
            if(DebugPrintEnabled)
            {
                DebugPrint("SD Card Removed");
            }

            if (OnMediaChanged != null)
            {
                OnMediaChanged(this, SDCard, false);
            }
        }

        #endregion

        
       

       

        #region Overrides of NetworkModule

        /// <summary>
        /// Gets a value that indicates whether this network is connected.
        /// </summary>
        /// <remarks>
        /// Derived classes override this property to return a value that indicates
        /// whether the media state is connected. This does not necessarily mean 
        /// that the network connection is functional.
        /// </remarks>
        public override bool IsNetworkConnected
        {
            get { return true; }
        }

        #endregion
    }
}
